package http;

import org.apache.http.client.fluent.Form;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by root on 15-6-2.
 */
public class Requests {
    private static Logger logger = LoggerFactory.getLogger(Requests.class);
    public final static Map<String, String> emptyHeaders = new HashMap<String, String>();
    public final static Map<String, String> disableNotifyHeader = new HashMap<String, String>();
    public final static Map<String, String> enableCheckStatusHeader = new HashMap<String, String>();
    public final static Map<String, String> realDeleteData = new HashMap<String, String>();
    // pre-set query
    public final static Map<String, String> emptyQuery = new HashMap<String, String>();
    // pre-set form
    public final static Map<String, String> emptyForm = new HashMap<String, String>();

    static {
        disableNotifyHeader.put("X-K2Data-hack-notify", "false");
        enableCheckStatusHeader.put("X-K2Data-hack-force-device-creation","false");
        realDeleteData.put("x-K2Data-mm-del-data","true");
    }

    public static SimpleHttpResponse get (String url, Map<String, String> headers, Map<String, String> query) throws http.RequestException {
        URIBuilder builder;
        try {
            builder = new URIBuilder(url);
            for (String s : query.keySet()) {
                builder.setParameter(s, query.get(s));
            }
        } catch (URISyntaxException e) {
            throw new http.RequestException(e.getMessage());
        }
        SimpleHttpResponse r = null;
        try {
            Request request = Request.Get(builder.build());
            for (String s : headers.keySet()) {
                request.addHeader(s, headers.get(s));
            }
//            request.s
            Response response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            // connection error
            throw new http.RequestException(null, e.getMessage());
        } catch (IllegalArgumentException e) {
            // invalid url
            throw new http.RequestException(null, e.getMessage());
        } catch (Exception e) {
            logger.warn("Unhandled exception");
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("GET {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("GET {}", url);
            }
        }
    }

    public static SimpleHttpResponse get (String url, Map<String, String> headers) throws http.RequestException {
        return get(url, headers, emptyQuery);
    }

    public static SimpleHttpResponse get (String url) throws http.RequestException {
        return get(url, emptyHeaders, emptyQuery);
    }

    public static SimpleHttpResponse checkGet (String url, Map<String, String> headers, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = get(url, headers);
        if (r.getStatusCode()!=expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected 200", r.getStatusCode());
            throw new http.RequestException(r, msg);
        }
        return r;
    }
    public static SimpleHttpResponse checkGet (String url, Map<String, String> headers) throws http.RequestException {
        return checkGet(url, headers, 200);
    }

    public static SimpleHttpResponse checkGet (String url, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = get(url);
        if (r.getStatusCode()!=expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected 200", r.getStatusCode());
            throw new http.RequestException(r, msg);
        }
        return r;
    }
    public static SimpleHttpResponse checkGet (String url) throws http.RequestException {
        return checkGet(url, 200);
    }


    public static SimpleHttpResponse post (String url, Map<String, String> headers, String body) throws http.RequestException {
        SimpleHttpResponse r = null;
        try {
            Request request = Request.Post(url).bodyString(body, ContentType.APPLICATION_JSON);
            for (String s : headers.keySet()) {
                request.addHeader(s, headers.get(s));
            }
            Response response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("POST {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("POST {}", url);
            }
            logger.info("BODY {}", body);
        }
    }

    public static SimpleHttpResponse post (String url, String body,
                                           Map<String, String> headers) throws http.RequestException {
        SimpleHttpResponse r = null;
        try {
            Request request = Request.Post(url).bodyString(body, ContentType.APPLICATION_JSON);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                request.addHeader(header.getKey(), header.getValue());
            }
            Response response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("POST {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("POST {}", url);
            }
            logger.info("BODY {}", body);
        }
    }

    public static SimpleHttpResponse post (String url, String body) throws http.RequestException {
        return post(url, emptyHeaders, body);
    }

    public static SimpleHttpResponse checkPost (String url, Map<String, String> headers, String body, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = post(url, headers, body);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPost (String url, Map<String, String> headers, String body) throws http.RequestException {
        return checkPost(url, headers, body, 201);
    }

    public static SimpleHttpResponse checkPost (String url, String body, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = post(url, body);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPost (String url, String body) throws http.RequestException {
        return checkPost(url, body, 201);
    }


    public static SimpleHttpResponse postForm (String url, Map<String, String> headers, Map<String, String> form) throws http.RequestException {
        SimpleHttpResponse r = null;
        Form body = Form.form();
        Set<String> keys = form.keySet();
        for (String key : keys) {
            body.add(key, form.get(key));
        }
        Response response = null;
        try {
            Request request = Request
                    .Post(url)
                    .bodyForm(body.build());
            for (String s : headers.keySet()) {
                request.addHeader(s, headers.get(s));
            }
            response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("POST {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("POST {}", url);
            }
            for (String key : form.keySet()) {
                logger.info("Form body {}={}", key, form.get(key));
            }
        }
    }

    public static SimpleHttpResponse postForm (String url, Map<String, String> form) throws http.RequestException {
        return postForm(url, emptyHeaders, form);

    }

    public static SimpleHttpResponse checkPostForm (String url, Map<String, String> headers, Map<String, String> form, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = postForm(url, headers, form);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPostForm (String url, Map<String, String> headers, Map<String, String> form) throws http.RequestException {
        return checkPostForm(url, headers, form, 201);
    }

    public static SimpleHttpResponse checkPostForm (String url, Map<String, String> form, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = postForm(url, form);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPostForm (String url, Map<String, String> form) throws http.RequestException {
        return checkPostForm(url, form, 201);
    }

    public static SimpleHttpResponse put (String url, Map<String, String> headers, String body) throws http.RequestException {
        SimpleHttpResponse r = null;
        try {
            Request request = Request.Put(url).bodyString(body, ContentType.APPLICATION_JSON);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                request.addHeader(header.getKey(), header.getValue());
            }
            Response response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("PUT {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("PUT {}", url);
            }
            logger.info("BODY {}", body);
        }
    }

    public static SimpleHttpResponse put (String url, String body,
                                          Map<String, String> headers) throws http.RequestException {
        SimpleHttpResponse r = null;
        try {
            Request request = Request.Put(url).bodyString(body, ContentType.APPLICATION_JSON);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                request.addHeader(header.getKey(), header.getValue());
            }
            Response response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("PUT {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("PUT {}", url);
            }
            logger.info("BODY {}", body);
        }
    }

    public static SimpleHttpResponse checkPut (String url, Map<String, String> headers, String body, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = put(url, headers, body);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPut (String url, Map<String, String> headers, String body) throws http.RequestException {
        return checkPut(url, headers, body, 201);
    }

    public static SimpleHttpResponse put (String url, String body) throws http.RequestException {
        return put(url, emptyHeaders, body);
    }

    public static SimpleHttpResponse checkPut (String url, String body, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = put(url, body);
        if (r.getStatusCode()!= expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected %s", r.getStatusCode(), expectedCode);
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkPut (String url, String body) throws http.RequestException {
        return checkPut(url, body, 201);
    }


    public static SimpleHttpResponse delete (String url, Map<String, String> headers) throws http.RequestException {
        SimpleHttpResponse r = null;
        Response response = null;
        try {
            Request request = Request.Delete(url);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                request.addHeader(header.getKey(), header.getValue());
            }
            response = request.execute();
            r = SimpleHttpResponse.from(response);
            return r;
        } catch (IOException e) {
            e.printStackTrace();
            throw new http.RequestException(null, e.getMessage());
        } finally {
            if (r!=null) {
                logger.info("DELETE {} {}", url, r.getStatusCode());
            }
            else {
                logger.info("DELETE " + url);
            }
        }
    }

    public static SimpleHttpResponse delete (String url) throws http.RequestException {
        return delete(url, emptyHeaders);
    }

    public static SimpleHttpResponse checkDelete (String url, Map<String, String> headers, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = delete(url, headers);
        if (r.getStatusCode()!=expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected 200", r.getStatusCode());
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkDelete (String url, Map<String, String> headers) throws http.RequestException {
        return checkDelete(url, headers, 200);
    }



    public static SimpleHttpResponse checkDelete (String url, int expectedCode) throws http.RequestException {
        SimpleHttpResponse r = delete(url);
        if (r.getStatusCode()!=expectedCode) {
            logger.error(r.toString());
            String msg = String.format("response code %s does not match expected 200", r.getStatusCode());
            throw new http.RequestException(r, msg);
        }
        return r;
    }

    public static SimpleHttpResponse checkDelete (String url) throws RequestException {
        return checkDelete(url, 200);
    }

}
